// Copyright (c) 2024 Huawei Device Co., Ltd. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/base/network_change_notifier_ohos.h"

#include <string>
#include <unordered_set>
#include <utility>

#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/task/task_traits.h"
#include "build/build_config.h"
#include "net/dns/dns_config_service_ohos.h"
#include "net/dns/system_dns_config_change_notifier.h"

namespace {
using namespace ohos::adapter::net;
using namespace net;

const int kNetDelayMilliseconds = 500;

class NetConnectionCallbackImpl : public NetConnectionCallback {
 public:
  explicit NetConnectionCallbackImpl(
      NetworkChangeNotifierOhos* network_change_notifier_ohos)
      : notifier_ohos_(network_change_notifier_ohos) {}
  virtual ~NetConnectionCallbackImpl() = default;
  int32_t NetAvailable() override;
  int32_t NetCapabilitiesChange(
      const NetConnectionType& netConnectType,
      const NetConnectionSubtype& netConnectSubtype) override;
  int32_t NetConnectionPropertiesChange() override;
  int32_t NetUnavailable() override;
  int32_t NetLost() override;

 private:
  raw_ptr<NetworkChangeNotifierOhos> notifier_ohos_ = nullptr;
};

int32_t NetConnectionCallbackImpl::NetAvailable() {
  return 0;
}

int32_t NetConnectionCallbackImpl::NetCapabilitiesChange(
    const NetConnectionType& netConnectType,
    const NetConnectionSubtype& netConnectSubtype) {
  if (notifier_ohos_) {
    notifier_ohos_->OnConnectionChanged(
        ConvertOhosConnTypeToNativeConnType<
            NetworkChangeNotifier::ConnectionType, NetConnectionType>(
            netConnectType));

    notifier_ohos_->OnConnectionSubtypeChanged(
        ConvertOhosConnTypeToNativeConnType<
            NetworkChangeNotifier::ConnectionType, NetConnectionType>(
            netConnectType),
        ConvertOhosConnTypeToNativeConnType<
            NetworkChangeNotifier::ConnectionSubtype, NetConnectionSubtype>(
            netConnectSubtype));
  }
  return 0;
}

int32_t NetConnectionCallbackImpl::NetConnectionPropertiesChange() {
  if (notifier_ohos_) {
    notifier_ohos_->OnDNSChanged();
    notifier_ohos_->OnIPAddressChanged();
  }
  return 0;
}

int32_t NetConnectionCallbackImpl::NetUnavailable() {
  if (notifier_ohos_) {
    notifier_ohos_->OnConnectionChanged(
        NetworkChangeNotifier::ConnectionType::CONNECTION_NONE);
    notifier_ohos_->OnConnectionSubtypeChanged(
        NetworkChangeNotifier::ConnectionType::CONNECTION_NONE,
        NetworkChangeNotifier::ConnectionSubtype::SUBTYPE_NONE);
  }
  return 0;
}

int32_t NetConnectionCallbackImpl::NetLost() {
  if (notifier_ohos_) {
    notifier_ohos_->OnConnectionChanged(
        NetworkChangeNotifier::ConnectionType::CONNECTION_NONE);
    notifier_ohos_->OnConnectionSubtypeChanged(
        NetworkChangeNotifier::ConnectionType::CONNECTION_NONE,
        NetworkChangeNotifier::ConnectionSubtype::SUBTYPE_NONE);
  }
  return 0;
}
}  // namespace

namespace net {

NetworkChangeNotifierOhos::NetworkChangeNotifierOhos(
    NetworkChangeNotifier::ConnectionType initial_connection_type,
    NetworkChangeNotifier::ConnectionSubtype initial_connection_subtype)
    : NetworkChangeNotifierOhos(initial_connection_type,
                                initial_connection_subtype,
                                /*system_dns_config_notifier=*/nullptr) {}

NetworkChangeNotifierOhos::NetworkChangeNotifierOhos(
    NetworkChangeNotifier::ConnectionType initial_connection_type,
    NetworkChangeNotifier::ConnectionSubtype initial_connection_subtype,
    SystemDnsConfigChangeNotifier* system_dns_config_notifier)
    : NetworkChangeNotifier(NetworkChangeCalculatorParamsOhos(),
                            system_dns_config_notifier),
      connection_type_(initial_connection_type),
      net_connection_adapter_(NetConnectionAdapterHelper::GetInstance()
                                  .CreateNetConnectionAdapter()),
      max_bandwidth_mbps_(
          NetworkChangeNotifier::GetMaxBandwidthMbpsForConnectionSubtype(
              initial_connection_subtype)) {
  net_connection_callback_ = std::make_shared<NetConnectionCallbackImpl>(this);
  if (net_connection_adapter_) {
    int32_t ret = net_connection_adapter_->RegisterNetConnectionCallback(
        net_connection_callback_);
    if (ret != 0) {
      LOG(ERROR) << "register ohos net connection callback failed.";
    }
  }
}

NetworkChangeNotifierOhos::~NetworkChangeNotifierOhos() {
  ClearGlobalPointer();
  if (net_connection_adapter_) {
    int32_t ret = net_connection_adapter_->UnregisterNetConnectionCallback(
        net_connection_callback_);
    if (ret != 0) {
      LOG(ERROR) << "unregister ohos net connection callback failed.";
    }
    net_connection_adapter_.reset();
  }
  if (net_connection_callback_ != nullptr) {
    net_connection_callback_.reset();
  }
}

void NetworkChangeNotifierOhos::OnDNSChanged() {
  GetCurrentSystemDnsConfigNotifier()->RefreshConfig();
}

void NetworkChangeNotifierOhos::OnIPAddressChanged() {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  NetworkChangeNotifier::NotifyObserversOfIPAddressChange();
}

void NetworkChangeNotifierOhos::OnConnectionChanged(
    NetworkChangeNotifier::ConnectionType connection_type) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  {
    base::AutoLock scoped_lock(lock_);
    connection_type_ = connection_type;
  }
  NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
}

void NetworkChangeNotifierOhos::OnConnectionSubtypeChanged(
    NetworkChangeNotifier::ConnectionType connection_type,
    NetworkChangeNotifier::ConnectionSubtype connection_subtype) {
  DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
  double max_bandwidth_mbps =
      GetMaxBandwidthMbpsForConnectionSubtype(connection_subtype);
  {
    base::AutoLock scoped_lock(lock_);
    max_bandwidth_mbps_ = max_bandwidth_mbps;
  }
  NetworkChangeNotifier::NotifyObserversOfMaxBandwidthChange(max_bandwidth_mbps,
                                                             connection_type);
}

NetworkChangeNotifier::ConnectionType
NetworkChangeNotifierOhos::GetCurrentConnectionType() const {
  base::AutoLock scoped_lock(lock_);
  return connection_type_;
}

void NetworkChangeNotifierOhos::GetCurrentMaxBandwidthAndConnectionType(
    double* max_bandwidth_mbps,
    ConnectionType* connection_type) const {
  base::AutoLock scoped_lock(lock_);
  *connection_type = connection_type_;
  *max_bandwidth_mbps = max_bandwidth_mbps_;
}

// static
NetworkChangeNotifier::NetworkChangeCalculatorParams
NetworkChangeNotifierOhos::NetworkChangeCalculatorParamsOhos() {
  NetworkChangeCalculatorParams params;
  params.ip_address_offline_delay_ = base::Milliseconds(kNetDelayMilliseconds);
  params.ip_address_online_delay_ = base::Milliseconds(kNetDelayMilliseconds);
  params.connection_type_offline_delay_ =
      base::Milliseconds(kNetDelayMilliseconds);
  params.connection_type_online_delay_ =
      base::Milliseconds(kNetDelayMilliseconds);
  return params;
}

}  // namespace net
